﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using System.Data;
using System.Globalization;
using TCSA.Graphics.GeomEngine;
using TCSA.Utils.Coordinates;
using System.Runtime.InteropServices;
using System.IO;
using System.Windows.Forms;
using System.Xml;
using Geoconcepts.Types;
using Google.KML;
using System.Drawing;


namespace Geoconcepts.SpatialServices
{

    public enum GenerationModes
    {
        ConvexHull,
        Delaunay
    }



    public class KmlServices
    {
        private int miHuso;

        [DllImport("AutoclustLibrary.dll")]
        public static extern int fnAutoclust(
            string pointsIn,
            double m,
            //   [MarshalAs(UnmanagedType.LPStr)] StringBuilder pointsOut,
           StringBuilder pointsOut,
            ref int numPoints);
        CultureInfo ciEnglish = new CultureInfo("en-US", true);
        private cFieldsDef moPolygonsFieldsDef;
        private cFieldsDef moPointsFieldsDef;
        private int miNumberOfPointsFound;

        public int NumberOfPointsFound
        {
            get { return miNumberOfPointsFound; }
        }
        private int miNumberOfPointsUsed;

        public int NumberOfPointsUsed
        {
            get { return miNumberOfPointsUsed; }
        }
        Random rnd = new Random();

        public KmlServices()
        {
            //Definición de las propiedades para los nuevos ficheros kml
            // Posiblemente me sobren y además no están todas las del formato kml
            cFieldDef oFieldDef;
            moPolygonsFieldsDef = new cFieldsDef(11);
            oFieldDef = new cFieldDef("Shape", ePropertyTypes.Geometry);
            moPolygonsFieldsDef.Add(oFieldDef);
            oFieldDef = new cFieldDef("feature", ePropertyTypes.Integer);
            moPolygonsFieldsDef.Add(oFieldDef);
            oFieldDef = new cFieldDef("mslink", ePropertyTypes.Integer);
            moPolygonsFieldsDef.Add(oFieldDef);
            oFieldDef = new cFieldDef("Snippet", ePropertyTypes.Geometry);
            moPolygonsFieldsDef.Add(oFieldDef);
            oFieldDef = new cFieldDef("description", ePropertyTypes.String);
            moPolygonsFieldsDef.Add(oFieldDef);
            oFieldDef = new cFieldDef("centroid", ePropertyTypes.String);
            moPolygonsFieldsDef.Add(oFieldDef);
            oFieldDef = new cFieldDef("References", ePropertyTypes.Integer);
            moPolygonsFieldsDef.Add(oFieldDef);
            oFieldDef = new cFieldDef("MainPart", ePropertyTypes.String);
            moPolygonsFieldsDef.Add(oFieldDef);
            oFieldDef = new cFieldDef("Area", ePropertyTypes.Double);
            moPolygonsFieldsDef.Add(oFieldDef);
            oFieldDef = new cFieldDef("LookAt", ePropertyTypes.String);
            moPolygonsFieldsDef.Add(oFieldDef);
            oFieldDef = new cFieldDef("MultyGeometry", ePropertyTypes.String);
            moPolygonsFieldsDef.Add(oFieldDef);

            moPointsFieldsDef = new cFieldsDef(4);
            oFieldDef = new cFieldDef("Shape", ePropertyTypes.Geometry);
            moPointsFieldsDef.Add(oFieldDef);
            oFieldDef = new cFieldDef("feature", ePropertyTypes.Integer);
            moPointsFieldsDef.Add(oFieldDef);
            oFieldDef = new cFieldDef("description", ePropertyTypes.String);
            moPointsFieldsDef.Add(oFieldDef);
            oFieldDef = new cFieldDef("location", ePropertyTypes.String);
            moPointsFieldsDef.Add(oFieldDef);
            oFieldDef = new cFieldDef("relevance", ePropertyTypes.Integer);
            moPointsFieldsDef.Add(oFieldDef);

            
        }

        #region Métodos públicos

        /// <summary>
        /// Crea un kml con los recintos generados a partir de las referencias obtenidas previamente de panoramio
        /// </summary>
        /// <param name="descriptions">Lista de referencias</param>
        /// <param name="path">Ruta de salida del kml</param>
        /// <param name="deviationFactor">Factor de desviación para calcular </param>
        /// <param name="references">Tabla de referncias</param>
        /// <param name="huso">Huso geográfico</param>
        /// <returns>Verdadero = éxito; false = fallo en la generación</returns>
        public bool CreateGisFileFromTable(ListBox.ObjectCollection descriptions, string path, string outputFile, double deviationFactor, DataTable references, int huso, GenerationModes generationMode)
        {
            try
            {
                string fileFormat = "shp";
                cGisFile oGisPolygonsFile = new cGisFile();
                cGisFile oGisPointsFile = new cGisFile();
                cGisFile oGisCentroidsFile = new cGisFile();

                string sPolygonsFile = path + "\\" + outputFile + "." + fileFormat;
                string sPointsFile = path + "\\" + outputFile + "_points." + fileFormat;
                string sCentroidsFile = path + "\\" + outputFile + "_centroids." + fileFormat;

                FileInfo fPolygonsKml = new FileInfo(sPolygonsFile);
                FileInfo fPointsKml = new FileInfo(sPointsFile);
                FileInfo fCentroidsKml = new FileInfo(sCentroidsFile);

                if (fPolygonsKml.Exists)
                {
                    fPolygonsKml.Delete();
                    if (fPointsKml.Exists)
                        fPointsKml.Delete();
                    if (fCentroidsKml.Exists)
                        fCentroidsKml.Delete();
                }
                oGisPolygonsFile.Create(sPolygonsFile, eShapeFileType.Polygon, false, moPolygonsFieldsDef, null);
                oGisPointsFile.Create(sPointsFile, eShapeFileType.Point, false, moPointsFieldsDef, null);
                oGisCentroidsFile.Create(sCentroidsFile, eShapeFileType.Point, false, moPointsFieldsDef, null);


                oGisPolygonsFile.Open(sPolygonsFile, false, false);
                oGisPointsFile.Open(sPointsFile, false, false);
                oGisCentroidsFile.Open(sCentroidsFile, false, false);

                foreach (object sPlace in descriptions)
                {

                    miHuso = huso;
                    cElements oElements = ReadDataTable(sPlace.ToString(), references);
                    miNumberOfPointsFound = oElements.Count;

                    AddElementsToGisFile(oElements, sPlace.ToString(), oGisPolygonsFile, oGisCentroidsFile, oGisPointsFile, deviationFactor, generationMode);
                }

                //cSpatialRelations oSpatialRelations = new cSpatialRelations(oGisPolygonsFile, miHuso);
                //oSpatialRelations.MakeRelations();

                oGisPolygonsFile.Close();
                oGisPointsFile.Close();
                oGisCentroidsFile.Close();
                //SortedList slColors = AddPolygonsSymbology(sPolygonsFile);
                //AddPointsSymbology(sPointsFile, slColors);
                //AddPointsSymbology(sCentroidsFile, slColors);

                return true;
            }
            catch (Exception exc)
            {
                //TCLog.WriteLine("Error al crear el KML a partir de una tabla en memoria: " + exc.Message);
                throw (exc);
            }

        }

        public void CreateKMLFileFromElements(cElements elements, string path, string outputFile, string fileFormat, int huso)
        {
            try
            {
                string sKMLFile = path + "\\" + outputFile + ".kml";
                //Si el fichero existe lo borro, yo no me voy a preocupar de comprobar esto
                FileInfo fsKMLFile = new FileInfo(sKMLFile);
                if (fsKMLFile.Exists)
                {
                    fsKMLFile.Delete();
                }
                geKML kml = CreateGeKMLFromElements(elements, outputFile, huso);

                File.WriteAllBytes(sKMLFile, kml.ToKML());
                
            }
            catch (Exception exc)
            {
                throw (exc);
            }
        }
        public XmlDocument CreateKMLDocumentFromElements(cElements elements, string outputFile, int huso)
        {
            try
            {
                XmlDocument oXML = new XmlDocument();

                geKML kml = CreateGeKMLFromElements(elements, outputFile, huso);
                byte []bKml = kml.ToKML();
                string sKml = System.Text.Encoding.ASCII.GetString(bKml);
                oXML.LoadXml(sKml);
                return oXML;
            }
            catch (Exception exc)
            {
                throw (exc);
            }
        }

        private geKML CreateGeKMLFromElements(cElements elements, string outputFile, int huso)
        {
            try
            {
                gePlacemark pmPoint;
                geCoordinates coords;
                gePoint point;

                //string sKMLFile = path + "\\" + outputFile + ".kml";
                ////Si el fichero existe lo borro, yo no me voy a preocupar de comprobar esto
                //FileInfo fsKMLFile = new FileInfo(sKMLFile);
                //if (fsKMLFile.Exists)
                //{
                //    fsKMLFile.Delete();
                //}

                geDocument oKMLDocument = CreateGisFileHeader(outputFile);

                geFolder polygonsFolder = new geFolder();
                polygonsFolder.Name = "Recintos";
                geFolder centroidsFolder = new geFolder();
                centroidsFolder.Name = "Centroides";
                //geFolder pointsFolder = new geFolder();
                //pointsFolder.Name = "Referencias";
                int iPolygonsCount = 0;
                foreach (cElement oElement in elements)
                {
                    double dArea = oElement.GetArea();
                    tPoint3D o3DCentroid = oElement.GetCentroid();
                    tPoint3D oGeoCentroid = SpatialConversions.ConvertToGeo(o3DCentroid, huso);

                    List<geCoordinates> polyCoords = new List<geCoordinates>();
                    foreach (tPoint3D o3DPoint in oElement.Geometry.pPoints)
                    {
                        tPoint3D o3DPointGeo = SpatialConversions.ConvertToGeo(o3DPoint, huso);
                        polyCoords.Add(new geCoordinates(new geAngle90(o3DPointGeo.y), new geAngle180(o3DPointGeo.x)));
                    }

                    //KML2 crea la línea a partir de las coordenadas
                    //KML2
                    gePlacemark pmPolygon = new gePlacemark();

                    geOuterBoundaryIs outer = new geOuterBoundaryIs(new geLinearRing(polyCoords));
                    gePolygon poly = new gePolygon(outer);
                    //lo añado al documento pegado al suelo
                    poly.AltitudeMode = geAltitudeModeEnum.clampToGround;

                    pmPolygon.StyleUrl = "#GeoconceptsPolygonSyle_" + oElement.Name;
                    pmPolygon.Name = oElement.Name;
                    //pmPolygon.Name = "hnv_" + strGeoConceptName;
                    pmPolygon.ID = iPolygonsCount.ToString();
                    pmPolygon.Description = ";Area=" + dArea +
                                    ";Centroid=" + oGeoCentroid.x +
                                    "," + oGeoCentroid.y;
                    pmPolygon.Geometry = poly;
                    polygonsFolder.Features.Add(pmPolygon);

                    pmPoint = new gePlacemark();
                    pmPoint.StyleUrl = "#GeoconceptsCentroidStyle_" + oElement.Name;
                    coords = new geCoordinates(new geAngle90(oGeoCentroid.y), new geAngle180(oGeoCentroid.x));
                    point = new gePoint(coords);
                    point.AltitudeMode = geAltitudeModeEnum.clampToGround;
                    pmPoint.Description = oElement.Name;
                    pmPoint.Name = oElement.Name;
                    pmPoint.Geometry = point;
                    pmPoint.ID = iPolygonsCount.ToString();

                    centroidsFolder.Features.Add(pmPoint);


                    iPolygonsCount++;

                    //AddElementsToKML2(oElements, sPlace.ToString(), polygonsFolder, pointsFolder, centroidsFolder, deviationFactor);
                }

                AddKmlStyles(oKMLDocument, polygonsFolder);

                oKMLDocument.Features.Add(polygonsFolder);
                oKMLDocument.Features.Add(centroidsFolder);
                //oKMLDocument.Features.Add(pointsFolder);
                geKML kml = new geKML(oKMLDocument);
                
                //File.WriteAllBytes(sKMLFile, kml.ToKML());

                return kml;

            }
            catch (Exception exc)
            {
                throw (exc);
            }

        }

        private geDocument CreateGisFileHeader(string outputFile)
        {
            try
            {                    
                geDocument oKMLDocument = new geDocument();
                oKMLDocument.Name = outputFile;
                //oKMLDocument.StyleSelectors.Add(defaultStyle);
                geFolder polygonsFolder = new geFolder();
                polygonsFolder.Name = "Recintos";
                geFolder centroidsFolder = new geFolder();
                centroidsFolder.Name = "Centroides";
                geFolder pointsFolder = new geFolder();
                pointsFolder.Name = "Referencias";


                //Añado los estilo básicos
                geStyle oVertexInStyle = new geStyle("GeoconceptsInsidePointStyle");
                oVertexInStyle.IconStyle = new geIconStyle();
                oVertexInStyle.IconStyle.TargetID = "http://maps.google.com/mapfiles/kml/shapes/placemark_circle.png";
                oVertexInStyle.IconStyle.Scale = 0.6F;
                oVertexInStyle.IconStyle.Color.SysColor = Color.Green;
                oVertexInStyle.LabelStyle = new geLabelStyle();
                oVertexInStyle.LabelStyle.Color.SysColor = Color.Transparent;
                //oVertexInStyle.LabelStyle = new geLabelStyle();
                //oVertexInStyle.LabelStyle.Color.SysColor = Color.Green;
                //oVertexInStyle.LabelStyle.Scale = 0.6F;
                oKMLDocument.StyleSelectors.Add(oVertexInStyle);
                geStyle oVertexOutStyle = new geStyle("GeoconceptsOutsidePointStyle");
                oVertexOutStyle.IconStyle = new geIconStyle();
                oVertexOutStyle.IconStyle.TargetID = "http://maps.google.com/mapfiles/kml/shapes/placemark_circle.png";
                oVertexOutStyle.IconStyle.Scale = 0.6F;
                oVertexOutStyle.IconStyle.Color.SysColor = Color.Red;
                oVertexOutStyle.LabelStyle = new geLabelStyle();
                oVertexOutStyle.LabelStyle.Color.SysColor = Color.Transparent;
                //oVertexOutStyle.LabelStyle = new geLabelStyle();
                //oVertexOutStyle.LabelStyle.Color.SysColor = Color.Red;
                //oVertexOutStyle.LabelStyle.Scale = 0.6F;
                oKMLDocument.StyleSelectors.Add(oVertexOutStyle);

                return oKMLDocument;
            }
            catch (Exception exc)
            {
                throw (exc);
            }
        }

        private void AddKmlStyles(geDocument oKMLDocument, geFolder polygonsFolder)
        {
            try
            {
                foreach (geFeature oFeature in polygonsFolder.Features)
                {

                    //string sHex = "ff" + String.Format("{0:X}", iFeat).ToLower();
                    int iColor = rnd.Next(16777215);
                    string sHex = "ff" + String.Format("{0:X}", iColor).ToLower();
                    int iCol = Convert.ToInt32(sHex, 16);
                    geStyle oPolygonGeoconceptStyle = new geStyle("GeoconceptsPolygonSyle_" + oFeature.Name);
                    oPolygonGeoconceptStyle.PolyStyle = new gePolyStyle();
                    oPolygonGeoconceptStyle.PolyStyle.Outline = true;
                    oPolygonGeoconceptStyle.PolyStyle.Fill = false;
                    oPolygonGeoconceptStyle.PolyStyle.Color.SysColor = Color.FromArgb(iCol);
                    //oPolygonGeoconceptStyle.PolyStyle.ColorMode = geColorModeEnum.random;
                    oPolygonGeoconceptStyle.LineStyle = new geLineStyle();
                    oPolygonGeoconceptStyle.LineStyle.Width = (float)2;
                    oPolygonGeoconceptStyle.LineStyle.Color.SysColor = oPolygonGeoconceptStyle.PolyStyle.Color.SysColor;
                    oKMLDocument.StyleSelectors.Add(oPolygonGeoconceptStyle);

                    geStyle oCentroidGeoconceptStyle = new geStyle("GeoconceptsCentroidStyle_" + oFeature.Name);
                    oCentroidGeoconceptStyle.IconStyle = new geIconStyle();
                    oCentroidGeoconceptStyle.IconStyle.Color.SysColor = oPolygonGeoconceptStyle.PolyStyle.Color.SysColor;
                    oCentroidGeoconceptStyle.IconStyle.Scale = 0.6F;
                    oCentroidGeoconceptStyle.IconStyle.TargetID = "http://maps.google.com/mapfiles/kml/shapes/placemark_circle.png";
                    oKMLDocument.StyleSelectors.Add(oCentroidGeoconceptStyle);
                }
            }
            catch (Exception exc)
            {
                throw (exc);
            }
        }

        /// <summary>
        /// Creates a KML 2.2 file from a DataTable containing a Geoconcept's references
        /// </summary>
        /// <param name="descriptions"></param>
        /// <param name="path"></param>
        /// <param name="outputFile"></param>
        /// <param name="fileFormat"></param>
        /// <param name="deviationFactor"></param>
        /// <param name="references"></param>
        /// <param name="huso"></param>
        /// <param name="generationMode"></param>
        /// <returns></returns>
        public bool CreateKML2FromTable(ListBox.ObjectCollection descriptions, string path, string outputFile, string fileFormat, double deviationFactor, DataTable references, int huso, GenerationModes generationMode)
        {
            try
            {
                string sKMLFile = path + "\\" + outputFile + ".kml";
                //Si el fichero existe lo borro, yo no me voy a preocupar de comprobar esto
                FileInfo fsKMLFile = new FileInfo(sKMLFile);
                if (fsKMLFile.Exists)
                {
                    fsKMLFile.Delete();
                }

                geDocument oKMLDocument = CreateGisFileHeader(outputFile);

                /*
                string sKMLFile = path + "\\" + outputFile + ".kml";
                geStyle defaultStyle;

                //Si el fichero existe lo borro, yo no me voy a preocupar de comprobar esto
                FileInfo fsKMLFile = new FileInfo(sKMLFile);
                if (fsKMLFile.Exists)
                {
                    fsKMLFile.Delete();
                }
                geDocument oKMLDocument = new geDocument();
                oKMLDocument.Name = outputFile;
                //oKMLDocument.StyleSelectors.Add(defaultStyle);
           


                //Añado los estilo básicos
                geStyle oVertexInStyle = new geStyle("GeoconceptsInsidePointStyle");
                oVertexInStyle.IconStyle = new geIconStyle();
                oVertexInStyle.IconStyle.TargetID = "http://maps.google.com/mapfiles/kml/shapes/placemark_circle.png";
                oVertexInStyle.IconStyle.Scale = 0.6F;
                oVertexInStyle.IconStyle.Color.SysColor = Color.Green;
                oVertexInStyle.LabelStyle = new geLabelStyle();
                oVertexInStyle.LabelStyle.Color.SysColor = Color.Transparent;
                //oVertexInStyle.LabelStyle = new geLabelStyle();
                //oVertexInStyle.LabelStyle.Color.SysColor = Color.Green;
                //oVertexInStyle.LabelStyle.Scale = 0.6F;
                oKMLDocument.StyleSelectors.Add(oVertexInStyle);
                geStyle oVertexOutStyle = new geStyle("GeoconceptsOutsidePointStyle");
                oVertexOutStyle.IconStyle = new geIconStyle();
                oVertexOutStyle.IconStyle.TargetID = "http://maps.google.com/mapfiles/kml/shapes/placemark_circle.png";
                oVertexOutStyle.IconStyle.Scale = 0.6F;
                oVertexOutStyle.IconStyle.Color.SysColor = Color.Red;
                oVertexOutStyle.LabelStyle = new geLabelStyle();
                oVertexOutStyle.LabelStyle.Color.SysColor = Color.Transparent;
                //oVertexOutStyle.LabelStyle = new geLabelStyle();
                //oVertexOutStyle.LabelStyle.Color.SysColor = Color.Red;
                //oVertexOutStyle.LabelStyle.Scale = 0.6F;
                oKMLDocument.StyleSelectors.Add(oVertexOutStyle);
                */
                geFolder polygonsFolder = new geFolder();
                polygonsFolder.Name = "Recintos";
                geFolder centroidsFolder = new geFolder();
                centroidsFolder.Name = "Centroides";
                geFolder pointsFolder = new geFolder();
                pointsFolder.Name = "Referencias";

                foreach (object sPlace in descriptions)
                {

                    miHuso = huso;
                    cElements oElements = ReadDataTable(sPlace.ToString(), references);
                    miNumberOfPointsFound = oElements.Count;

                    AddElementsToKML2(oElements, sPlace.ToString(), polygonsFolder, pointsFolder, centroidsFolder, deviationFactor);
                }

                //Añado un estilo para cada geoconcepto
                AddKmlStyles(oKMLDocument, polygonsFolder);

                /*
                foreach (geFeature oFeature in polygonsFolder.Features)
                {

                    //string sHex = "ff" + String.Format("{0:X}", iFeat).ToLower();
                    int iColor = rnd.Next(16777215);
                    string sHex = "ff" + String.Format("{0:X}", iColor).ToLower();
                    int iCol = Convert.ToInt32(sHex, 16);
                    geStyle oPolygonGeoconceptStyle = new geStyle("GeoconceptsPolygonSyle_" + oFeature.Name);
                    oPolygonGeoconceptStyle.PolyStyle = new gePolyStyle();
                    oPolygonGeoconceptStyle.PolyStyle.Outline = true;
                    oPolygonGeoconceptStyle.PolyStyle.Fill = false;
                    oPolygonGeoconceptStyle.PolyStyle.Color.SysColor = Color.FromArgb(iCol);
                    //oPolygonGeoconceptStyle.PolyStyle.ColorMode = geColorModeEnum.random;
                    oPolygonGeoconceptStyle.LineStyle = new geLineStyle();
                    oPolygonGeoconceptStyle.LineStyle.Width = (float)2;
                    oPolygonGeoconceptStyle.LineStyle.Color.SysColor = oPolygonGeoconceptStyle.PolyStyle.Color.SysColor;
                    oKMLDocument.StyleSelectors.Add(oPolygonGeoconceptStyle);

                    geStyle oCentroidGeoconceptStyle = new geStyle("GeoconceptsCentroidStyle_" + oFeature.Name);
                    oCentroidGeoconceptStyle.IconStyle = new geIconStyle();
                    oCentroidGeoconceptStyle.IconStyle.Color.SysColor = oPolygonGeoconceptStyle.PolyStyle.Color.SysColor;
                    oCentroidGeoconceptStyle.IconStyle.Scale = 0.6F;
                    oCentroidGeoconceptStyle.IconStyle.TargetID = "http://maps.google.com/mapfiles/kml/shapes/placemark_circle.png";
                    oKMLDocument.StyleSelectors.Add(oCentroidGeoconceptStyle);
                }
                */
                oKMLDocument.Features.Add(polygonsFolder);
                oKMLDocument.Features.Add(centroidsFolder);
                oKMLDocument.Features.Add(pointsFolder);
                geKML kml = new geKML(oKMLDocument);
                File.WriteAllBytes(sKMLFile, kml.ToKML());
                
                return true;
            }
            catch (Exception exc)
            {
                throw (exc);
            }
        }
        #endregion

        #region Métodos privados
        /// <summary>
        /// Adds polygons related to a Geoconcept to the KML 2.2 file
        /// </summary>
        /// <param name="elements"></param>
        /// <param name="description"></param>
        /// <param name="oKMLDocument"></param>
        /// <param name="polygonsFolder"></param>
        /// <param name="pointsFolder"></param>
        /// <param name="centroidsFolder"></param>
        /// <param name="dDeviationFactor"></param>
        /// <returns></returns>
        private bool AddElementsToKML2(cElements elements, string description,
                        geFolder polygonsFolder, geFolder pointsFolder, geFolder centroidsFolder, double dDeviationFactor)
        {
            try
            {
                if (elements.Count == 0)
                    return false;

                cElements oPolygons = new cElements();
                cElements oCentroids = new cElements();



                string[] strSplit = description.ToString().Split('|');
                string strGeoConceptName = strSplit[0].Trim();

                cElement oGeoHull = null;
                //el mismo elemento en UTMs para hacer operaciones geométricas
                cElement oUTMHull = null;
                cElements oUTMHulls = new cElements();

                //Puntos para el KML
                gePlacemark pmPoint;
                geCoordinates coords;
                gePoint point;

                string inP = "";
                foreach (cElement oElement in elements)
                {
                    inP += oElement.Geometry.pPoints[0].x.ToString() + ";" + oElement.Geometry.pPoints[0].y.ToString() + ";";
                }
                inP = inP.Replace(",", ".");
                int iNumPoints = 0;
                StringBuilder outPoints = new StringBuilder(100000);

                //Genera los polígono a partir de una nube de puntos
                int j = fnAutoclust(inP, dDeviationFactor, outPoints, ref iNumPoints);

                string sPoints = outPoints.ToString();
                //Los polígonos están separados por un carácter #
                string[] sPolygons = sPoints.Split('#');
                int iPolygonsCount = 0;

                //Para cada geoconcepto puede haber varios polígonos
                foreach (string sPolygon in sPolygons)
                {
                    if (sPolygon == "")
                        continue;


                    //GisFile
                    oUTMHull = new cElement();
                    oUTMHull.Type = eGeometryTypes.Polygon;
                    oUTMHull.pProperties = new cProperty[11];
                    for (int i = 0; i < oUTMHull.pProperties.Length; i++)
                    {
                        oUTMHull.pProperties[i] = new cProperty();
                        oUTMHull.pProperties[i].Name = moPolygonsFieldsDef[i].Name;
                        oUTMHull.pProperties[i].Type = moPolygonsFieldsDef[i].Type;
                    }
                    oUTMHull.pProperties.Initialize();
                    oUTMHull.pProperties[cProperty.IndexOfProperty("description", oUTMHull.pProperties)].Value = strGeoConceptName;
                    oUTMHull.pProperties[cProperty.IndexOfProperty("References", oUTMHull.pProperties)].Value = elements.Count;
                    string sFirstCoord = sPolygon.Substring(0, sPolygon.IndexOf(';'));
                    string sPolygonTemp = sPolygon + sFirstCoord;
                    string[] sCoordenadas = sPolygonTemp.Split(';');

                    List<geCoordinates> polyCoords = new List<geCoordinates>();

                    foreach (string sPoint in sCoordenadas)
                    {
                        if (sPoint == "")
                            continue;

                        string[] sCoordenada = sPoint.Split(',');
                        double dX = Convert.ToDouble(sCoordenada[0].Replace(".", ",").Trim());
                        double dY = Convert.ToDouble(sCoordenada[1].Replace(".", ",").Trim());

                        //GisFile
                        tPoint3D o3DPoint = new tPoint3D();
                        o3DPoint.x = Convert.ToDouble(dX);
                        o3DPoint.y = Convert.ToDouble(dY);
                        oUTMHull.Geometry.Add(o3DPoint, false);

                        //KML2
                        tPoint3D o3DPointGeo = SpatialConversions.ConvertToGeo(o3DPoint, miHuso);
                        polyCoords.Add(new geCoordinates(new geAngle90(o3DPointGeo.y), new geAngle180(o3DPointGeo.x)));

                    }

                    //Gis element
                    oUTMHull.Geometry.FixCounterClockwiseRings();
                    oUTMHull.Geometry.CloseRings();

                    if (oUTMHull == null)
                        return false;

                    //para medir el área lo tengo que pasar a UTMs
                    double dArea = oUTMHull.GetArea();
                    oUTMHull.pProperties[cProperty.IndexOfProperty("Area", oUTMHull.pProperties)].Value = dArea;
                    oUTMHull.pProperties[cProperty.IndexOfProperty("MainPart", oUTMHull.pProperties)].Value = "Sec";
                    tPoint3D o3DCentroid = oUTMHull.GetCentroid();
                    oUTMHull.pProperties[cProperty.IndexOfProperty("Centroid", oUTMHull.pProperties)].Value = o3DCentroid.x.ToString() + ";" + o3DCentroid.y.ToString();
                    oUTMHull.Name = strGeoConceptName;
                    oUTMHulls.Add(oUTMHull);


                    oGeoHull = SpatialConversions.ConvertToGeo(oUTMHull, miHuso);
                    oPolygons.Add(oGeoHull);




                    #region Centroides
                    //Calculo el centroide
                    cElement oCentroid = new cElement();
                    oCentroid.Type = eGeometryTypes.Point;
                    oCentroid.Geometry.Add(o3DCentroid);

                    oCentroid.Name = strGeoConceptName;
                    //oCentroid.Name = "hnv_" + strGeoConceptName;
                    cElement oGeoCentroid = SpatialConversions.ConvertToGeo(oCentroid, miHuso);
                    oCentroids.Add(oCentroid);



                    pmPoint = new gePlacemark();
                    pmPoint.StyleUrl = "#GeoconceptsCentroidStyle_" + strGeoConceptName;
                    coords = new geCoordinates(new geAngle90(oGeoCentroid.Geometry.pPoints[0].y), new geAngle180(oGeoCentroid.Geometry.pPoints[0].x));
                    point = new gePoint(coords);
                    point.AltitudeMode = geAltitudeModeEnum.clampToGround;
                    pmPoint.Description = strGeoConceptName;
                    pmPoint.Name = strGeoConceptName;
                    pmPoint.Geometry = point;
                    pmPoint.ID = iPolygonsCount.ToString();

                    centroidsFolder.Features.Add(pmPoint);

                    #endregion




                    #region Polígono
                    //KML2 crea la línea a partir de las coordenadas
                    //KML2
                    gePlacemark pmPolygon = new gePlacemark();

                    geOuterBoundaryIs outer = new geOuterBoundaryIs(new geLinearRing(polyCoords));
                    gePolygon poly = new gePolygon(outer);
                    //lo añado al documento pegado al suelo
                    poly.AltitudeMode = geAltitudeModeEnum.clampToGround;

                    pmPolygon.StyleUrl = "#GeoconceptsPolygonSyle_" + strGeoConceptName;
                    pmPolygon.Name = strGeoConceptName;
                    //pmPolygon.Name = "hnv_" + strGeoConceptName;
                    pmPolygon.ID = iPolygonsCount.ToString();
                    pmPolygon.Description = ";Area=" + dArea +
                                    ";Centroid=" + oGeoCentroid.Geometry.pPoints[0].x +
                                    "," + oGeoCentroid.Geometry.pPoints[0].y;
                    pmPolygon.Geometry = poly;
                    polygonsFolder.Features.Add(pmPolygon);
                    #endregion

                    iPolygonsCount++;
                }
                if (iPolygonsCount == 0)
                    return false;
                #region Vértices
                //compruebo si los puntos están dentro y los guardo
                foreach (cElement oPointElement in elements)
                {

                    bool bIsContained;

                    bIsContained = false;
                    foreach (cElement oUTMCheckHull in oUTMHulls)
                    {
                        if (!bIsContained)
                        {
                            bIsContained = oUTMCheckHull.CheckSpatial(eSearchingModes.Contains, oPointElement);
                            if (!bIsContained)
                                bIsContained = oUTMCheckHull.CheckSpatial(eSearchingModes.Touches, oPointElement);

                        }
                    }
                    string sPointStyle;
                    if (bIsContained)
                    {
                        sPointStyle = "#GeoconceptsInsidePointStyle";
                    }
                    else
                    {
                        sPointStyle = "#GeoconceptsOutsidePointStyle";
                    }
                    oPointElement.Name = strGeoConceptName;
                    cElement oGeoPointElement = SpatialConversions.ConvertToGeo(oPointElement, miHuso);


                    pmPoint = new gePlacemark();
                    pmPoint.StyleUrl = sPointStyle;
                    coords = new geCoordinates(new geAngle90(oGeoPointElement.Geometry.pPoints[0].y), new geAngle180(oGeoPointElement.Geometry.pPoints[0].x));
                    point = new gePoint(coords);
                    point.AltitudeMode = geAltitudeModeEnum.clampToGround;
                    pmPoint.Description = strGeoConceptName;
                    pmPoint.Name = strGeoConceptName;
                    pmPoint.Geometry = point;
                    pointsFolder.Features.Add(pmPoint);

                }
                #endregion

                /*Las relaciones se harán en un porceso posterior
                #region Área
                //Compruebo cuál es el de mayor área
                double dMaxArea = 0.0;
                int iLargest = 0;
                cElement oLargestElement = null;
                for (int i = 0; i < oPolygons.Count; i++)
                {
                    cElement oSavedElement = oPolygons[i];
                    if (oSavedElement.Name != strGeoConceptName)
                        continue;
                    double dArea = (double)oSavedElement.pProperties[cProperty.IndexOfProperty("Area", oSavedElement.pProperties)].Value;
                    if (dArea > dMaxArea)
                    {
                        oLargestElement = oSavedElement;
                        dMaxArea = dArea;
                        iLargest = i;
                    }
                }
                oLargestElement.pProperties[cProperty.IndexOfProperty("MainPart", oLargestElement.pProperties)].Value = "Main";

                //Cambio el nombre al elemento mayor del Geoconcept en el KML2
                gePlacemark geBiggestPlaceMark = GetPlaceMark("hnv_" + strGeoConceptName, iLargest.ToString(), polygonsFolder);
                geBiggestPlaceMark.Name = strGeoConceptName;
                gePlacemark geBiggestPlaceMarkCentroid = GetPlaceMark("hnv_" + strGeoConceptName, iLargest.ToString(), centroidsFolder);
                geBiggestPlaceMarkCentroid.Name = strGeoConceptName;

                //gisPolygonsFile.Save();

                #endregion
                */
                return true;
            }
            catch (Exception exc)
            {
                throw (exc);
            }

        }
        /// <summary>
        /// Crea un fichero kml con la envolvente de una serie de puntos
        /// </summary>
        /// <param name="elements">Conjunto de puntos</param>
        /// <param name="description">descripción de la envolvente</param>
        /// <param name="path">Ruta del nuevo fichero kml</param>
        /// <returns>Se ha creado o no</returns>
        private bool AddElementsToGisFile(cElements elements, string description, cGisFile gisPolygonsFile,
            cGisFile gisCentroidsFile, cGisFile gisPointsFile, double dDeviationFactor, GenerationModes generationMode)
        {
            try
            {
                if (elements.Count == 0)
                    return false;

                string[] strSplit = description.ToString().Split('|');
                string strGeoConceptName = strSplit[0].Trim();

                cElement oGeoHull = null;
                //el mismo elemento en UTMs para hacer operaciones geométricas
                cElement oUTMHull = null;
                cElements oUTMHulls = new cElements();

      
                #region Delaunay
               
                    string inP = "";
                    foreach (cElement oElement in elements)
                    {
                        inP += oElement.Geometry.pPoints[0].x.ToString() + ";" + oElement.Geometry.pPoints[0].y.ToString() + ";";
                    }
                    inP = inP.Replace(",", ".");
                    int iNumPoints = 0;
                    StringBuilder outPoints = new StringBuilder(100000);

                    int j = fnAutoclust(inP, dDeviationFactor, outPoints, ref iNumPoints);

                    string sPoints = outPoints.ToString();

                    string[] sPolygons = sPoints.Split('#');
                    int iPolygonsCount = 0;
                    foreach (string sPolygon in sPolygons)
                    {
                        if (sPolygon == "")
                            continue;

                        oUTMHull = new cElement();
                        oUTMHull.Type = eGeometryTypes.Polygon;
                        oUTMHull.pProperties = new cProperty[11];
                        for (int i = 0; i < oUTMHull.pProperties.Length; i++)
                        {
                            oUTMHull.pProperties[i] = new cProperty();
                            oUTMHull.pProperties[i].Name = moPolygonsFieldsDef[i].Name;
                            oUTMHull.pProperties[i].Type = moPolygonsFieldsDef[i].Type;
                        }
                        oUTMHull.pProperties.Initialize();
                        oUTMHull.pProperties[cProperty.IndexOfProperty("description", oUTMHull.pProperties)].Value = strGeoConceptName;
                        oUTMHull.pProperties[cProperty.IndexOfProperty("References", oUTMHull.pProperties)].Value = elements.Count;
                        string sFirstCoord = sPolygon.Substring(0, sPolygon.IndexOf(';'));
                        string sPolygonTemp = sPolygon + sFirstCoord;

                        string[] sCoordenadas = sPolygonTemp.Split(';');


                        foreach (string sPoint in sCoordenadas)
                        {
                            if (sPoint == "")
                                continue;

                            string[] sCoordenada = sPoint.Split(',');
                            double dX = Convert.ToDouble(sCoordenada[0].Replace(".", ",").Trim());
                            double dY = Convert.ToDouble(sCoordenada[1].Replace(".", ",").Trim());

                            tPoint3D o3DPoint = new tPoint3D();
                            o3DPoint.x = Convert.ToDouble(dX);
                            o3DPoint.y = Convert.ToDouble(dY);
                            oUTMHull.Geometry.Add(o3DPoint, false);
                        }

                        oUTMHull.Geometry.FixCounterClockwiseRings();
                        oUTMHull.Geometry.CloseRings();
                        //para medir el área lo tengo que pasar a UTMS
                        double dArea = oUTMHull.GetArea();
                        oUTMHull.pProperties[cProperty.IndexOfProperty("Area", oUTMHull.pProperties)].Value = dArea;
                        oUTMHull.pProperties[cProperty.IndexOfProperty("MainPart", oUTMHull.pProperties)].Value = "Sec";
                        tPoint3D o3DCentroid = oUTMHull.GetCentroid();
                        oUTMHull.pProperties[cProperty.IndexOfProperty("Centroid", oUTMHull.pProperties)].Value = o3DCentroid.x.ToString() + ";" + o3DCentroid.y.ToString();
                        oUTMHull.Name = strGeoConceptName;
                        oUTMHulls.Add(oUTMHull);


                        if (gisPolygonsFile.SourceType == eSourceTypes.KmlsFile)
                        {
                            oGeoHull = SpatialConversions.ConvertToGeo(oUTMHull, miHuso);
                            gisPolygonsFile.AppendElement(oGeoHull, true);
                        }
                        else
                        {
                            gisPolygonsFile.AppendElement(oUTMHull, true);
                        }
                        iPolygonsCount++;
                        cElement oCentroid = new cElement();
                        oCentroid.Type = eGeometryTypes.Point;
                        oCentroid.Geometry.Add(o3DCentroid);
                        oCentroid.pProperties = new cProperty[4];
                        for (int i = 0; i < oCentroid.pProperties.Length; i++)
                        {
                            oCentroid.pProperties[i] = new cProperty();
                            oCentroid.pProperties[i].Name = moPointsFieldsDef[i].Name;
                            oCentroid.pProperties[i].Type = moPointsFieldsDef[i].Type;
                        }
                        oCentroid.pProperties.Initialize();
                        oCentroid.pProperties[cProperty.IndexOfProperty("description", oCentroid.pProperties)].Value = strGeoConceptName;
                        oCentroid.pProperties[cProperty.IndexOfProperty("location", oCentroid.pProperties)].Value = "Centroid";
                        oCentroid.Name = strGeoConceptName;
                        if (gisCentroidsFile.SourceType == eSourceTypes.KmlsFile)
                        {
                            cElement oGeoCentroid = SpatialConversions.ConvertToGeo(oCentroid, miHuso);
                            gisCentroidsFile.AppendElement(oGeoCentroid, true);
                        }
                        else
                        {
                            gisCentroidsFile.AppendElement(oCentroid, true);
                        }
                    }
                #endregion
                    if (iPolygonsCount == 0)
                        return false;

                    double dMaxArea = 0.0;
                    int iLargest = 0;
                    cElement oLargestElement = null;

                    for (int i = 0; i < gisPolygonsFile.FeatureCursor.NumElements; i++)
                    {

                        cElement oSavedElement = gisPolygonsFile.FeatureCursor[i];
                        if (oSavedElement.Name != strGeoConceptName)
                            continue;
                        double dArea = (double)oSavedElement.pProperties[cProperty.IndexOfProperty("Area", oSavedElement.pProperties)].Value;
                        if (dArea > dMaxArea)
                        {
                            oLargestElement = oSavedElement;
                            dMaxArea = dArea;

                        }

                    }

                    oLargestElement.pProperties[cProperty.IndexOfProperty("MainPart", oLargestElement.pProperties)].Value = "Main";
                    gisPolygonsFile.Save();

                    #region points
                    //compruebo si los puntos están dentro y los guardo
                    foreach (cElement oPointElement in elements)
                    {
                        oPointElement.pProperties = new cProperty[4];
                        for (int i = 0; i < oPointElement.pProperties.Length; i++)
                        {
                            oPointElement.pProperties[i] = new cProperty();
                            oPointElement.pProperties[i].Name = moPointsFieldsDef[i].Name;
                            oPointElement.pProperties[i].Type = moPointsFieldsDef[i].Type;
                        }
                        oPointElement.pProperties.Initialize();
                        oPointElement.pProperties[cProperty.IndexOfProperty("description", oPointElement.pProperties)].Value = strGeoConceptName;
                        bool bIsContained;
                       
                    
                        bIsContained = false;
                        foreach (cElement oUTMCheckHull in oUTMHulls)
                        {
                            bIsContained = oUTMCheckHull.CheckSpatial(eSearchingModes.Contains, oPointElement);
                            if (!bIsContained)
                                bIsContained = oUTMCheckHull.CheckSpatial(eSearchingModes.Touches, oPointElement);
                            if (bIsContained)
                                break;
                        }
                      
                        if (bIsContained)
                            oPointElement.pProperties[cProperty.IndexOfProperty("location", oPointElement.pProperties)].Value = "Inside";
                        else
                            oPointElement.pProperties[cProperty.IndexOfProperty("location", oPointElement.pProperties)].Value = "Outside";
                        oPointElement.Name = strGeoConceptName;
                        if (gisPointsFile.SourceType == eSourceTypes.KmlsFile)
                        {
                            cElement oGeoPointElement = SpatialConversions.ConvertToGeo(oPointElement, miHuso);
                            gisPointsFile.AppendElement(oGeoPointElement, true);
                        }
                        else
                        {
                            gisPointsFile.AppendElement(oPointElement, true);
                        }
                    }
               
                    #endregion


                return true;
            }
            catch (Exception exc)
            {
                throw (exc);
            }

        }


        /// <summary>
        /// Obtiene un placeMark dentro de una carpeta a partir de su nombre
        /// Seguro que se puede haceer más fácil con folder.Features.Find pero no hay documentación
        /// Devuelvo el primero que tenga el ID y el nombre buscados
        /// </summary>
        /// <param name="name"></param>
        /// <param name="folder"></param>
        /// <returns></returns>
        private gePlacemark GetPlaceMark(string name, string id, geFolder folder)
        {
            try
            {
                foreach (geFeature geFeatureIter in folder.Features)
                {
                    if (geFeatureIter.Name.Equals(name) && geFeatureIter.ID.Equals(id))
                    {
                        return (gePlacemark)geFeatureIter;
                    }
                }
                return null;
            }
            catch (Exception exc)
            {
                throw (exc);
            }
        }

        /// <summary>
        /// Filtra y lee los puntos obtenidos de panoramio de una tabla cargada en memoria para una referencia
        /// </summary>
        /// <param name="description"></param>
        /// <param name="references"></param>
        /// <returns>Elementos gráficos </returns>
        private cElements ReadDataTable(string description, DataTable references)
        {
            try
            {
                cElements oPointElements = new cElements();

                string[] strSplit = description.ToString().Split('|');
                string strGeoConceptName = strSplit[0].Trim();

                DataRow[] drPoints = SelectPointsFromReference(strSplit[1], references);

                foreach (DataRow drPoint in drPoints)
                {
                    cElement oPointElement = new cElement();

                    drPoint["Description"] = strGeoConceptName;
                    drPoint.AcceptChanges();
                    tPoint3D oGeoPoint = new tPoint3D(Convert.ToDouble(drPoint["X"]), Convert.ToDouble(drPoint["Y"]));
                    tPoint3D oUTMPoint = SpatialConversions.ConvertToUTM(oGeoPoint, miHuso);

                    oPointElement.Geometry.Insert(0, oUTMPoint);
                    oPointElements.Add(oPointElement);
                }
                references.AcceptChanges();
                return oPointElements;
            }
            catch (Exception exc)
            {
                throw(exc);
            }
        }

        /// <summary>
        /// Selecciona todas las coordenadas de una referencia
        /// </summary>
        /// <param name="description"></param>
        /// <param name="references"></param>
        /// <returns></returns>
        private DataRow[] SelectPointsFromReference(string description, DataTable references)
        {
            try
            {
                references.CaseSensitive = false;
                string[] sTags = description.Split(",".ToCharArray());
                string sSql = "";
                for (int j = 0; j < sTags.Length; j++)
                {
                    if (j > 0)
                        sSql = sSql + " OR ";
                    string[] descriptionParts = sTags[j].Trim().Split(" ".ToCharArray());
                    sSql = sSql + "(PhotoTitle like '%" + descriptionParts[0].ToLower() + "%'";
                    for (int i = 1; i < descriptionParts.Length; i++)
                    {
                        sSql = sSql + " AND PhotoTitle like '%" + descriptionParts[i].ToLower() + "%'";
                    }
                    sSql += ")";
                }

                DataRow[] drPoints = references.Select(sSql);
                return drPoints;
            }
            catch (Exception exc)
            {
                //  TCLog.WriteLine("Error al obtener los puntos para una referencia: " + exc.Message);
                return null;
            }
        }



        private void AddGeoconceptsRelation(string source, string target, double overlapping, GeoconceptsDataSet dsGeoconcepts)
        {
            try
            {
                if (overlapping > 0)
                {
                    GeoconceptsDataSet.GeoconceptsRelationsRow drGeoconceptsRelation = dsGeoconcepts.GeoconceptsRelations.NewGeoconceptsRelationsRow();
                    drGeoconceptsRelation.Geoconcept = source;
                    drGeoconceptsRelation.GeoconceptTarget = target;
                    if (overlapping > 0.85)
                    {
                        drGeoconceptsRelation.RelationShip = "isSpatialPartOf_High";
                    }
                    if (overlapping > 0.70 && overlapping <= 0.85)
                    {
                        drGeoconceptsRelation.RelationShip = "isSpatialPartOf_Low";
                    }
                    if (overlapping > 0.50 && overlapping <= 0.70)
                    {
                        drGeoconceptsRelation.RelationShip = "isSpatialNeighbour_High";
                    }
                    if (overlapping > 0.25 && overlapping <= 0.50)
                    {
                        drGeoconceptsRelation.RelationShip = "isSpatialNeighbour_Medium";
                    }
                    if (overlapping > 0.0 && overlapping <= 0.25)
                    {
                        drGeoconceptsRelation.RelationShip = "isSpatialNeighbour_Low";
                    }
                    dsGeoconcepts.GeoconceptsRelations.AddGeoconceptsRelationsRow(drGeoconceptsRelation);
                }
            }
            catch (Exception exc)
            {
                throw (exc);
            }
        }

        /// <summary>
        /// Añado un geoconcepto si no existe
        /// </summary>
        /// <param name="geoConceptName"></param>
        private void AddGeoconcept(string geoConceptName, GeoconceptsDataSet dsGeoconcepts)
        {
            try
            {
                //Añado el Geoconcept source al dataset aquí ya debo saber si es hnv o no
                //Si es geoconcepto nuevo lo añado
                DataRow[] drGeoconcepts = dsGeoconcepts.GeoconceptsItems.Select("Geoconcept='" + geoConceptName + "'");
                GeoconceptsDataSet.GeoconceptsItemsRow drGeoConcept;
                if (drGeoconcepts.Length == 0)
                {
                    drGeoConcept = dsGeoconcepts.GeoconceptsItems.NewGeoconceptsItemsRow();
                    drGeoConcept.Geoconcept = geoConceptName;
                    drGeoConcept.NumPoints = "0";
                    drGeoConcept.ElevationMax = "0";
                    drGeoConcept.ElevationMin = "0";
                    dsGeoconcepts.GeoconceptsItems.AddGeoconceptsItemsRow(drGeoConcept);
                }
                else
                {
                    drGeoConcept = (GeoconceptsDataSet.GeoconceptsItemsRow)drGeoconcepts[0];
                }                                       
            }
            catch (Exception exc)
            {
                throw (exc);
            }
        }

        /// <summary>
        /// Añade una relación has nice views si no existe
        /// </summary>
        /// <param name="sourceGeoConceptName"></param>
        /// <param name="targetGeoConceptName"></param>
        /// <param name="relation"></param>
        private void AddHnvRelation(string sourceGeoConceptName, string targetGeoConceptName, string relation, GeoconceptsDataSet dsGeoconcepts)
        {
            try
            {
                DataRow[] drRelations = dsGeoconcepts.GeoconceptsRelations.Select("Geoconcept='" + sourceGeoConceptName+ "' AND GeoconceptTarget= '" + targetGeoConceptName + "' AND RelationShip='" + relation + "'");
                if(drRelations.Length >0)
                    return;

                GeoconceptsDataSet.GeoconceptsRelationsRow drGeoconceptsRelation = dsGeoconcepts.GeoconceptsRelations.NewGeoconceptsRelationsRow();
                drGeoconceptsRelation.Geoconcept = sourceGeoConceptName;
                drGeoconceptsRelation.GeoconceptTarget = targetGeoConceptName;
                drGeoconceptsRelation.RelationShip = relation;
                dsGeoconcepts.GeoconceptsRelations.AddGeoconceptsRelationsRow(drGeoconceptsRelation);

            }
            catch(Exception exc)
            {
                throw (exc);
            }
        }


      
        /// <summary>
        /// Obtiene un GeoconceprsDatasSet a partir de un fichero kml
        /// </summary>
        /// <param name="kmlFile"></param>
        /// <returns></returns>
        public GeoconceptsDataSet GetDataSetFromGisFile(string gisFilePath)
        {
            try
            {
                GeoconceptsDataSet dsGeoconcepts = new GeoconceptsDataSet();

                //Añado las posibles relaciones (Esto deberá estar en el contructor)
                dsGeoconcepts.RelationShips.AddRelationShipsRow("hasNiceViewsOf_High");
                dsGeoconcepts.RelationShips.AddRelationShipsRow("hasNiceViewsOf_Medium");
                dsGeoconcepts.RelationShips.AddRelationShipsRow("hasNiceViewsOf_Low");
                dsGeoconcepts.RelationShips.AddRelationShipsRow("isSpatialPartOf_High");
                dsGeoconcepts.RelationShips.AddRelationShipsRow("isSpatialPartOf_Medium");
                dsGeoconcepts.RelationShips.AddRelationShipsRow("isSpatialPartOf_Low");
                dsGeoconcepts.RelationShips.AddRelationShipsRow("isSpatialNeighbour_High");
                dsGeoconcepts.RelationShips.AddRelationShipsRow("isSpatialNeighbour_Medium");
                dsGeoconcepts.RelationShips.AddRelationShipsRow("isSpatialNeighbour_Low");
                dsGeoconcepts.RelationShips.AddRelationShipsRow("isSpatiallyEqual_Low");
                dsGeoconcepts.RelationShips.AcceptChanges();

                cGisFile oGisFile = new cGisFile();
                oGisFile.Open(gisFilePath, true, false);
                cElements oSourceElements = oGisFile.ToElements();
                cElements oTargetElements = oGisFile.ToElements();
                if (oSourceElements.Count == 0)
                {
                    return dsGeoconcepts;
                }



                //In order to work make spatial operations we translate the coordinates to UTM refered to an 'arbitrary' UTM Zone
                //This zone is the one containing the centroid of the first element
                int miHuso = SpatialConversions.CalculateUTMZone(oSourceElements[0].GetCentroid());

                
                //Recorro los polígonos
                foreach (cElement oSourceElement in oSourceElements)
                {
                    cElement oUTMSourceElement;
                    oUTMSourceElement = SpatialConversions.ConvertToUTM(oSourceElement, miHuso);
                    oUTMSourceElement.MakeTo2D();
                    tPoint3D oSourceCentroid = SpatialConversions.ConvertToUTM(oSourceElement.GetCentroid(), miHuso);

                    //  if (oUTMSourceElement.pProperties[cProperty.IndexOfProperty("MainPart", oUTMSourceElement.pProperties)].Value.ToString() != "Main")
                    //      continue;
                    double dSourceArea = oUTMSourceElement.GetArea();
                    
                    string sGeoConceptName = oUTMSourceElement.pProperties[cProperty.IndexOfProperty("description", oUTMSourceElement.pProperties)].Value.ToString();
                    //string sGeoConceptName = oUTMSourceElement.Name;

                    //Número de referencias
                    double iMaxPoints = 0;                    
                    cElement oMain = null;

                    AddGeoconcept(sGeoConceptName, dsGeoconcepts);

                    //Establezco como principal para un geoconcepto el que contenga más puntos
                    foreach (cElement oTargetElement in oTargetElements)
                    {
                        //Lo comparo con los que ya están anteriores
                        if(oTargetElement.Equals(oSourceElement))
                            break;
                        
                        cElement oUTMTargetElement = SpatialConversions.ConvertToUTM(oTargetElement, miHuso);
                        oUTMTargetElement.MakeTo2D();
                        tPoint3D oTargetCentroid = SpatialConversions.ConvertToUTM(oTargetElement.GetCentroid(), miHuso);

                        string sTargetGeoConceptName = oUTMTargetElement.pProperties[cProperty.IndexOfProperty("description", oUTMTargetElement.pProperties)].Value.ToString();
                        double dTargetArea = oUTMTargetElement.GetArea();

                        //Si no pertenecen al mismo geoconcepto compruebo si son contenido-vecino

                        if (!sTargetGeoConceptName.Equals(sGeoConceptName))
                        {
                            if (oUTMSourceElement.pProperties[cProperty.IndexOfProperty("MainPart", oUTMSourceElement.pProperties)].Value.ToString() == "Main" &&
                                oUTMTargetElement.pProperties[cProperty.IndexOfProperty("MainPart", oUTMTargetElement.pProperties)].Value.ToString() == "Main")
                            {
                                double dIntersectionArea;
                                //Calculo la intersección
                                eGeometryTypes eResultType;

                                if(oUTMSourceElement.CheckSpatial(eSearchingModes.Overlaps, oUTMTargetElement))
                                {
                                    //AddGeoconceptsRelation(sGeoConceptName, sTargetGeoConceptName, 0.6, dsGeoconcepts);
                                    //AddGeoconceptsRelation(sTargetGeoConceptName, sGeoConceptName, 0.6, dsGeoconcepts);

                                    cParts oIntersection = oUTMSourceElement.MakeSpatialOperation(eSpatialOperationTypes.Intersection, oUTMTargetElement);


                                    if (oIntersection != null)
                                    {
                                        dIntersectionArea = oIntersection.GetArea();

                                        double dSourceOverlapping = dIntersectionArea / dSourceArea;
                                        double dTargetOverlapping = dIntersectionArea / dSourceArea;

                                        AddGeoconceptsRelation(sGeoConceptName, sTargetGeoConceptName, dSourceOverlapping, dsGeoconcepts);
                                        AddGeoconceptsRelation(sTargetGeoConceptName, sGeoConceptName, dTargetOverlapping, dsGeoconcepts);
                                        
                                    }
                                }
                                
                                 
   
                               
                            }
                        }

                    //Si dos polígonos pertenecen al mismo geoconcepto
                        else
                        {
                            double dCentroidsDistance = Math.Sqrt(Math.Pow((oSourceCentroid.x - oTargetCentroid.x), 2) + Math.Pow((oSourceCentroid.y - oTargetCentroid.y), 2));

                            //Si el source es secundario compruebo si el principal ya está añadido para ver si el sec en un hnv
                            if (oUTMSourceElement.pProperties[cProperty.IndexOfProperty("MainPart", oUTMSourceElement.pProperties)].Value.ToString() == "Main" &&
                                oUTMTargetElement.pProperties[cProperty.IndexOfProperty("MainPart", oUTMTargetElement.pProperties)].Value.ToString() == "Sec")
                            {
                                if (Math.Sqrt(dSourceArea) < 2 * dCentroidsDistance)
                                {
                                    
                                    sTargetGeoConceptName = "hnv_" + sTargetGeoConceptName;
                                    AddGeoconcept(sTargetGeoConceptName, dsGeoconcepts);
                                    AddHnvRelation(sTargetGeoConceptName, sGeoConceptName, "hasNiceViewsOf_High", dsGeoconcepts);
                                    //GeoconceptsDataSet.GeoconceptsRelationsRow drGeoconceptsRelation = dsGeoconcepts.GeoconceptsRelations.NewGeoconceptsRelationsRow();
                                    //drGeoconceptsRelation.Geoconcept = sTargetGeoConceptName;
                                    //drGeoconceptsRelation.GeoconceptTarget = sGeoConceptName;
                                    //drGeoconceptsRelation.RelationShip = "hasNiceViewsOf_High";
                                    //dsGeoconcepts.GeoconceptsRelations.AddGeoconceptsRelationsRow(drGeoconceptsRelation);
                                    
                                }
                            }
                            else if (oUTMTargetElement.pProperties[cProperty.IndexOfProperty("MainPart", oUTMSourceElement.pProperties)].Value.ToString() == "Main" &&
                                oUTMSourceElement.pProperties[cProperty.IndexOfProperty("MainPart", oUTMTargetElement.pProperties)].Value.ToString() == "Sec")
                            {
                                if (Math.Sqrt(dTargetArea) < 2 * dCentroidsDistance)
                                {                                    
                                    sGeoConceptName = "hnv_" + sGeoConceptName;
                                    AddGeoconcept(sGeoConceptName, dsGeoconcepts);
                                    AddHnvRelation(sGeoConceptName, sTargetGeoConceptName, "hasNiceViewsOf_High", dsGeoconcepts);

                                    //GeoconceptsDataSet.GeoconceptsRelationsRow drGeoconceptsRelation = dsGeoconcepts.GeoconceptsRelations.NewGeoconceptsRelationsRow();
                                    //drGeoconceptsRelation.Geoconcept = sGeoConceptName;
                                    //drGeoconceptsRelation.GeoconceptTarget = sTargetGeoConceptName;
                                    //drGeoconceptsRelation.RelationShip = "hasNiceViewsOf_High";
                                    //dsGeoconcepts.GeoconceptsRelations.AddGeoconceptsRelationsRow(drGeoconceptsRelation);

                                }
                            }

                        }

                        //Añado el Geoconcept source al dataset aquí ya debo saber si es hnv o no
                        ////Si es geoconcepto nuevo lo añado
                        //DataRow[] drGeoconcepts = dsGeoconcepts.GeoconceptsItems.Select("Geoconcept='" + sGeoConceptName + "'");
                        //GeoconceptsDataSet.GeoconceptsItemsRow drGeoConcept;
                        //if (drGeoconcepts.Length == 0)
                        //{
                        //    drGeoConcept = dsGeoconcepts.GeoconceptsItems.NewGeoconceptsItemsRow();
                        //    drGeoConcept.Geoconcept = sGeoConceptName;
                        //    drGeoConcept.NumPoints = "0";
                        //    drGeoConcept.ElevationMax = "0";
                        //    drGeoConcept.ElevationMin = "0";
                        //    dsGeoconcepts.GeoconceptsItems.AddGeoconceptsItemsRow(drGeoConcept);
                        //}
                        //else
                        //{
                        //    drGeoConcept = (GeoconceptsDataSet.GeoconceptsItemsRow)drGeoconcepts[0];
                        //}                                       
                        //Añado un polígono
                        DataRow[] drPolygons = dsGeoconcepts.Polygons.Select("Polygon like 'p_" + sGeoConceptName + "%'");

                        GeoconceptsDataSet.PolygonsRow drNewPolygon = dsGeoconcepts.Polygons.NewPolygonsRow();
                        drNewPolygon.ElevationMax = "0";
                        drNewPolygon.ElevationMin = "0";
                        drNewPolygon.Geoconcept = sGeoConceptName;
                        drNewPolygon.NumPoints = oUTMSourceElement.Geometry.NumPoints.ToString();
                        drNewPolygon.Polygon = "p_" + sGeoConceptName + Convert.ToString(drPolygons.Length + 1);
                        drNewPolygon.Points = "";
                        foreach (tPoint3D oPoint in oUTMSourceElement.Geometry.pPoints)
                        {
                            drNewPolygon.Points += oPoint.x.ToString() + " " + oPoint.y.ToString() + " ";
                        }
                        drNewPolygon.Points.Trim();
                        dsGeoconcepts.Polygons.AddPolygonsRow(drNewPolygon);

                        drNewPolygon.AcceptChanges();

                        DataRow[] drGeoconcepts = dsGeoconcepts.GeoconceptsItems.Select("Geoconcept='" + sGeoConceptName + "'");
                        GeoconceptsDataSet.GeoconceptsItemsRow drGeoConcept;
                        if (drGeoconcepts.Length == 0)
                        {
                            drGeoConcept = (GeoconceptsDataSet.GeoconceptsItemsRow)drGeoconcepts[0];
                            drGeoConcept.NumPoints = Convert.ToString(Convert.ToInt32(drGeoConcept.NumPoints) + oUTMSourceElement.Geometry.NumPoints);
                            drGeoConcept.AcceptChanges();

                        }


                        //Añado el centroide
                        GeoconceptsDataSet.CentroidsRow drNewCentroid = dsGeoconcepts.Centroids.NewCentroidsRow();
                        drNewCentroid.Centroid = "c_" + sGeoConceptName + Convert.ToString(drPolygons.Length + 1);
                        drNewCentroid.Point = oUTMSourceElement.GetCentroid().x + " " + oUTMSourceElement.GetCentroid().y;
                        drNewCentroid.Polygon = drNewPolygon.Polygon;
                        drNewCentroid.Elevation = "0";
                        dsGeoconcepts.Centroids.AddCentroidsRow(drNewCentroid);
                    }

                }
                dsGeoconcepts.AcceptChanges();

                oGisFile.Close();

                return dsGeoconcepts;

            }
            catch (Exception exc)
            {
                throw (exc);
            }
        }


        #endregion

        #region For Testing purposes
        /// <summary>
        /// Elimina los puntos lejanos según su desviación estándar
        /// </summary>
        /// <param name="elements">Conjunto de puntos</param>
        private int RemovePoints(cElements elements, double dDeviationFactor)
        {
            int iOldPointCount = elements.Count;
            //Encuentro el centroide
            tPoint3D oCentroid = new tPoint3D();
            // oCentroid = elements.GetCentroid(eCentroidSearchingModes.Within);
            Double xsum = 0.0;
            Double ysum = 0.0;
            for (int i = 0; i < elements.Count; i++)
            {
                xsum = xsum + elements[i].Geometry.pPoints[0].x;
                ysum = ysum + elements[i].Geometry.pPoints[0].y;
            }
            Double xaverage;
            Double yaverage;
            xaverage = xsum / elements.Count;
            yaverage = ysum / elements.Count;
            oCentroid.x = xaverage;
            oCentroid.y = yaverage;
            //Elimino los puntos dispersos
            //Calculo las desviaciones estándar
            double[] dDistances = new double[elements.Count];
            double dSumDistances = 0;
            for (int i = 0; i < elements.Count; i++)
            {
                dDistances[i] = elements[i].GetMinVertexDistance2D(oCentroid);
                dSumDistances = dSumDistances += dDistances[i];
            }
            double dMean = dSumDistances / elements.Count;
            double dSumSqrDistance = 0;
            for (int i = 0; i < elements.Count; i++)
            {
                dSumSqrDistance += Math.Pow(dDistances[i], 2);
            }
            double dStdDeviation = Math.Sqrt((dSumSqrDistance / elements.Count) - Math.Pow(dMean, 2));

            if (elements.Count > 0)
            {
                for (int i = elements.Count - 1; i > -1; i--)
                {
                    if (Math.Abs(dDistances[i] - dMean) > dStdDeviation * dDeviationFactor)
                        elements.RemoveAt(i);
                }
            }
            return elements.Count;
        }
        #endregion
    }
    
}
